package com.kyubi.common.grammar;

import java.util.HashSet;
import java.util.Set;
import java.util.Vector;

/**
 * This class represents the Tree datastructure used to represent the 
 * syntax tree.
 * @author anand
 * @see com.kyubi.common.grammar.TreeNode
 */
public class Tree {

	private Set<TreeNode> nodes = new HashSet<TreeNode>();
	private TreeNode root = null;
	private Vector<String> words = null;
	private Vector<String> tags = null;
	private StringBuffer taggedSentence = null;
	private String id = null;
	private String line = null;

	/**
	 * Initialize the data structures
	 */
	private void init() {
		words = new Vector<String>();
		tags = new Vector<String>();
		taggedSentence = new StringBuffer();
	}

	/**
	 * Function to parse a given String representation of a syntax tree
	 * and create the Tree structure.
	 * @param line	the tree String representation
	 * @return	the root TreeNode
	 */
	public TreeNode parse(String line) {
		init();

		this.line = line;

		StringBuffer sID = new StringBuffer();
		Vector<TreeNode> stack = new Vector<TreeNode>();
		Vector<TreeNode> parents = new Vector<TreeNode>();

		boolean skip = false;
		boolean rootSet = false;

		StringBuffer token = new StringBuffer();
		for (int i = 0; i < line.length(); i++) {
			char char_at_i = line.charAt(i);

			// if you encounter an opening brace the previously
			// connected chars are a token
			// push it into stack
			if (char_at_i == '(' || char_at_i == ' ') {
				// // stupid moronic Stanford parser coders - special handling
				// for
				// // 'CD (8)' kind of tags
				// if (stack.size() > 0
				// && stack.lastElement().getHead().equals("CD")) {
				// continue;
				// }

				if (token.length() > 0) {
					String str = token.toString().trim();
					sID.append(str + " ");

					TreeNode tNode = new TreeNode(str);
					nodes.add(tNode);

					if (!rootSet) {
						root = tNode;
						rootSet = true;
					}

					stack.add(tNode);
					parents.add(tNode);

					token = new StringBuffer();
				}
			} else if (char_at_i == ')') {
				if (token.length() > 0) {
					String str = token.toString().trim();
					TreeNode tNode = new TreeNode(str);
					tNode.setText(str);
					nodes.add(tNode);

					stack.add(tNode);
					// parents.add(tNode);
					if (parents.lastElement() != null) {
						taggedSentence.append(str + "_"
								+ parents.lastElement().getHead() + " ");
						words.add(tNode.getHead());
						tags.add(parents.lastElement().getHead());
					}

					token = new StringBuffer();
				}

				// // stupid moronic Stanford parser coders - special handling
				// for
				// // 'CD (8)' kind of tags
				// if (stack.size() > 0
				// && stack.lastElement().getHead().equals("CD")) {
				// continue;
				// }

				// else if the char is ')'
				// pop items from stack until element == top element on parent
				// stack

				try {
					if (parents.size() > 0) {
						while (stack.lastElement() != parents.lastElement()) {
							TreeNode cNode = stack.remove(stack.size() - 1);
							cNode.setParent(parents.lastElement());
							parents.lastElement().addChild(cNode);
						}
						
						parents.lastElement().populateText();

						if (stack.lastElement() == parents.lastElement()) {
							parents.remove(parents.size() - 1);
							// root = parents.remove(parents.size() - 1);
							// root.populateText();
						}
					}
				} catch (Exception e) {
					e.printStackTrace();
				}

			} else if (char_at_i == '[') {
				skip = true;
			} else if (char_at_i == ']') {
				skip = false;
			} else {
				// else add char to token
				if (char_at_i != ' ' && skip != true) {
					token.append(char_at_i);
				}
			}
		}

		root.populateText();
		this.id = sID.toString().trim();
		// setPOSTaggedSentence();
		return root;
	}

	// private void setPOSTaggedSentence() {
	// init();
	// StringBuffer str = new StringBuffer();
	// Vector<TreeNode> queue = new Vector<TreeNode>();
	// queue.add(root);
	//		
	// if(root.getChildren().size() == 0) {
	// return;
	// }
	//
	// while (!queue.isEmpty()) {
	// TreeNode tNode = queue.remove(0);
	//
	// Vector<TreeNode> children = tNode.getChildren();
	// if (children.size() > 0) {
	// for (int i = children.size() - 1; i >= 0; i--) {
	// queue.add(children.get(i));
	// }
	// } else {
	// str.append(tNode.getHead() + "_" + tNode.getParent().getHead()
	// + " ");
	// words.add(tNode.getHead());
	// tags.add(tNode.getParent().getHead());
	// }
	// }
	//
	// taggedSentence = str.toString();
	// }

	/**
	 * Function to return only the WORDS in the syntax tree (not the POS tags)
	 * @return	the vector of words
	 */
	public Vector<String> getOriginalSentence() {
		return words;
	}

	/**
	 * Function to return the POS tags of the syntax tree
	 * @return	the vector of POS tags
	 */
	public Vector<String> getPOSTags() {
		return tags;
	}

	/**
	 * Function to return the sentence with POS tags
	 * @return	the POS tagged sentence
	 */
	public String getPOSTaggedSentence() {
		if (taggedSentence != null) {
			return taggedSentence.toString();
		}

		// setPOSTaggedSentence();
		return taggedSentence.toString();
	}

	/**
	 * Function to return only those TreeNodes which match the given tag
	 * @param tag	the tag to match
	 * @return	the TreeNodes which have the tag
	 */
	public Vector<TreeNode> getTagNodes(String tag) {
		Vector<TreeNode> tNodes = new Vector<TreeNode>();

		for (TreeNode node : this.nodes) {
			if (node.getHead().equals(tag)) {
				tNodes.add(node);
			}
		}

		return tNodes;
	}

	/**
	 * Function to return the complete list of TreeNodes of this Tree
	 * @return	the set of nodes
	 */
	public Set<TreeNode> getNodes() {
		return this.nodes;
	}

	/**
	 * Function to return the unique identifier for this Tree
	 * @return	the unique ID
	 */
	public String getID() {
		return this.id;
	}
	
	/**
	 * Function to check if this Tree represents a valid syntax tree
	 * @return	true	if valid
	 * @return	false	otherwise
	 */
	public boolean isValid() {
		if(root != null) {
			if(root.getChildren() != null) {
				Vector<TreeNode> children = root.getChildren();
				
				if(children.get(0).getHead().equals("S")) {
					return true;
				}
			}
		}
		
		return false;
	}

	/**
	 * Main function for testing
	 */
	public static void main(String[] args) {
		try {
			Tree tree = new Tree();
			tree
					.parse("(ROOT [109.489] (S [104.261] (VP [103.128] (VB [4.199] do) (RB [0.269] n't) (VP [93.829] (VB [4.040] buy) (NP [15.048] (DT [3.859] this) (NN [9.205] camera)) (SBAR [69.890] (IN [2.772] if) (S [66.792] (NP [4.591] (PRP [3.341] you)) (VP [61.869] (VBP [0.128] are) (RB [1.571] not) (VP [53.479] (VBG [3.631] going) (S [47.462] (VP [47.197] (TO [0.011] to) (VP [47.167] (VB [5.924] purchase) (NP [39.209] (DT [1.419] a) (CD [9.288] 128) (NN [10.787] megabyte) (NN [8.254] card))))))))))) (. [0.002] .)))");
			System.out.println(tree.getPOSTaggedSentence());
			System.out.println(tree.getID());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
